/*
 * OpenParts
 * A dynamic-loading components framework for GWT
 * Copyright (C) 2011 Christophe Bouthier  [chris{AT}binary-gastronome{DOT}fr]
 *
 * This work is partially based on work I have done at INRIA (http://www.inria.fr) 
 * in the context of the Qualipso European Project (http://qualipso.org/),
 * The work done at INRIA is Copyright (C) 2006-2011 INRIA
 *
 * This work is distributed under the LGPL version 3
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation. See the GNU
 * Lesser General Public License in LGPL.txt for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package fr.openparts.OpenParts.test.gwt.tests;

import com.google.gwt.junit.client.GWTTestCase;

import fr.openparts.OpenParts.client.OPPartLoadingRequest;
import fr.openparts.OpenParts.client.OPPartsLoaderBinding;

/**
 * GWT tests for OPPartsLoaderBinding.
 * 
 * @author Christophe Bouthier [chris{AT}binary-gastronome{DOT}fr]
 * @creation.date 23 January 2010
 */
public class OPPartsLoaderBindingTestGwt extends GWTTestCase {

    private static final String SERVICE_NAME = "service";

    private OPPartsLoaderBinding binding;
    private OPPartLoadingRequest request;
    private OPPartsLoaderMocked partsLoader;

    /**
     * Return the name of the tested GWT Module.
     * 
     * @see com.google.gwt.junit.client.GWTTestCase#getModuleName()
     */
    @Override
    public final String getModuleName() {
        return "fr.openparts.OpenParts.test.gwt.TestOpenParts";
    }

    /**
     * Called at the start of each test to set up the action.
     * 
     * @see com.google.gwt.junit.client.GWTTestCase#gwtSetUp()
     */
    @Override
    protected void gwtSetUp() throws Exception {
        super.gwtSetUp();
        binding = OPPartsLoaderBinding.partsLoaderBindingFactory();
        request = OPPartLoadingRequest.partLoadingRequestFactory(SERVICE_NAME, null);
        partsLoader = new OPPartsLoaderMocked();
    }

    /**
     * Test creation of parts loader binding. Should get something not null.
     */
    public final void testCreatingPartsLoaderBinding() {
        assertNotNull("parts loader binding shouldn't be null", binding);
    }

    /**
     * Test executing a null loading request. Should raise an assertion error.
     */
    public final void testExecutingNullLoadingRequest() {
        if (assertionsAreEnabled()) {
            try {
                binding.executeLoadingRequest(null);
                fail("OPPartsLoaderBinding should raise an assertion error when trying to execute a null loading request.");
            } catch (AssertionError e) {
                assertTrue("Error should be an AssertionError", e instanceof AssertionError);
            }
        }
    }

    /**
     * Test executing a loading request when the parts loader is not binded. Should NOT raise an error.
     */
    public final void testExecutingLoadingRequestWithoutPartsLoader() {
        try {
            binding.executeLoadingRequest(request);
        } catch (Throwable e) {
            fail("OPPartsLoaderBinding should NOT raise an error when executing a loading request without parts loader first binded.");
        }
    }

    /**
     * Test executing a loading request. Should call the (mocked) parts loader.
     */
    public final void testExecutingLoadingRequest() {
        registerPartsLoader(binding, partsLoader);
        assertEquals("executeLoadingRequest should not have been called yet", 0, partsLoader.executeLoadingRequestCalled);
        assertEquals("notifyLoadingCompleted should not have been called yet", 0, partsLoader.notifyLoadingCompletedCalled);
        assertEquals("isPartLoaded should not have been called yet", 0, partsLoader.isPartLoadedCalled);
        binding.executeLoadingRequest(request);
        assertEquals("executeLoadingRequest should have been called now", 1, partsLoader.executeLoadingRequestCalled);
        assertEquals("notifyLoadingCompleted should still not have been called", 0, partsLoader.notifyLoadingCompletedCalled);
        assertEquals("isPartLoaded should still not have been called", 0, partsLoader.isPartLoadedCalled);
    }

    /**
     * Test notifying that a null service completed loading. Should raise an assertion error.
     */
    public final void testNotifyingLoadingCompletedWithNullRequest() {
        if (assertionsAreEnabled()) {
            try {
                binding.notifyLoadingCompleted(null);
                fail("OPPartsLoaderBinding should raise an assertion error when notifying that a null service completed loading.");
            } catch (AssertionError e) {
                assertTrue("Error should be an AssertionError", e instanceof AssertionError);
            }
        }
    }

    /**
     * Test notifying that a service completed loading when the parts loader is not binded. Should NOT raise an error.
     */
    public final void testNotifyingLoadingCompletedWithoutPartLoader() {
        try {
            binding.notifyLoadingCompleted("test");
        } catch (Throwable e) {
            fail("OPPartsLoaderBinding should NOT raise an error when notifying that a service completed loading without parts loader first binded.");
        }
    }

    /**
     * Test notifying that a service completed loading. Should call the (mocked) parts loader.
     */
    public final void testNotifyingLoadingCompleted() {
        registerPartsLoader(binding, partsLoader);
        assertEquals("executeLoadingRequest should not have been called yet", 0, partsLoader.executeLoadingRequestCalled);
        assertEquals("notifyLoadingCompleted should not have been called yet", 0, partsLoader.notifyLoadingCompletedCalled);
        assertEquals("isPartLoaded should not have been called yet", 0, partsLoader.isPartLoadedCalled);
        binding.notifyLoadingCompleted(SERVICE_NAME);
        assertEquals("executeLoadingRequest should still not have been called", 0, partsLoader.executeLoadingRequestCalled);
        assertEquals("notifyLoadingCompleted should have been called now", 1, partsLoader.notifyLoadingCompletedCalled);
        assertEquals("isPartLoaded should still not have been called", 0, partsLoader.isPartLoadedCalled);
    }
    
    /**
     * Test checking if a null service name part is loaded. Should raise an assertion error.
     */
    public final void testCheckingIfPartIsLoadedWithNullServiceName() {
        if (assertionsAreEnabled()) {
            try {
                binding.isPartLoaded(null);
                fail("OPPartsLoaderBinding should raise an assertion error when checking if a part is loaded with a null service name.");
            } catch (AssertionError e) {
                assertTrue("Error should be an AssertionError", e instanceof AssertionError);
            }
        }
    }
    
    /**
     * Test checking if a part is loaded when the parts loader is not binded. Should NOT raise an error.
     */
    public final void testCheckingIfPartIsLoadedWithoutPartsLoader() {
        try {
            binding.isPartLoaded(SERVICE_NAME);
        } catch (Throwable e) {
            fail("OPPartsLoaderBinding should NOT raise an error when checking if a part is loaded without parts loader first binded.");
        }
        assertFalse("Checking if part is loaded without parts loader should return false", binding.isPartLoaded(SERVICE_NAME));
    }
    
    /**
     * Test checking if a part is loaded. Should call the (mocked) parts loader.
     */
    public final void testCheckingIfPartIsLoaded() {
        registerPartsLoader(binding, partsLoader);
        assertEquals("executeLoadingRequest should not have been called yet", 0, partsLoader.executeLoadingRequestCalled);
        assertEquals("notifyLoadingCompleted should not have been called yet", 0, partsLoader.notifyLoadingCompletedCalled);
        assertEquals("isPartLoaded should not have been called yet", 0, partsLoader.isPartLoadedCalled);
        assertTrue("Checking if dummy service is loaded should return true", binding.isPartLoaded(SERVICE_NAME));
        assertEquals("executeLoadingRequest should still not have been called", 0, partsLoader.executeLoadingRequestCalled);
        assertEquals("notifyLoadingCompleted should still not have been called", 0, partsLoader.notifyLoadingCompletedCalled);
        assertEquals("isPartLoaded should have been called now", 1, partsLoader.isPartLoadedCalled);
    }

    /**
     * Mock class for parts loader.
     */
    final class OPPartsLoaderMocked {
        public int executeLoadingRequestCalled = 0;
        public int notifyLoadingCompletedCalled = 0;
        public int isPartLoadedCalled = 0;
        public boolean partIsLoaded = true;
    }

    /**
     * Register this class as a mock OPPartsLoader in the given binding.
     * 
     * @param binding
     *            the binding on which to register
     */
    private final native void registerPartsLoader(final OPPartsLoaderBinding binding, final OPPartsLoaderMocked partLoader) /*-{
        binding.executeLoadingRequest = function(service) {
            ++partLoader.@fr.openparts.OpenParts.test.gwt.tests.OPPartsLoaderBindingTestGwt.OPPartsLoaderMocked::executeLoadingRequestCalled;
        };
        binding.notifyLoadingCompleted = function(service) {
            ++partLoader.@fr.openparts.OpenParts.test.gwt.tests.OPPartsLoaderBindingTestGwt.OPPartsLoaderMocked::notifyLoadingCompletedCalled;
        };
        binding.isPartLoaded = function(service) {
            ++partLoader.@fr.openparts.OpenParts.test.gwt.tests.OPPartsLoaderBindingTestGwt.OPPartsLoaderMocked::isPartLoadedCalled;
            return partLoader.@fr.openparts.OpenParts.test.gwt.tests.OPPartsLoaderBindingTestGwt.OPPartsLoaderMocked::partIsLoaded;
        };
    }-*/;

    /**
     * Check if assertions are enabled.
     * 
     * @return true if assertions are enabled, false otherwise
     */
    private final boolean assertionsAreEnabled() {
        try {
            assert false;
            return false;
        } catch (AssertionError e) {
            return true;
        }
    }
}
